﻿using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace Common
{
    public static class TaskHelper
    {
        private static async Task RunTaskInternal<TResult>(ConcurrentQueue<TaskItem<TResult>> queue, TResult[] results)
        {
            while (queue.TryDequeue(out var item))
            {
                results[item.Index] = await item.Item();
                if (queue.IsEmpty)
                    break;
            }
        }

        public static Task<TResult[]> RunTasks<TResult>(Func<Task<TResult>>[] tasks, int multiple = 8)
        {
            if (multiple < 1)
                throw new ArgumentOutOfRangeException(nameof(multiple));

            if (tasks.Length < 1)
                throw new ArgumentOutOfRangeException(nameof(tasks));

            var threadNum = tasks.Length < multiple ? tasks.Length : multiple;

            var taskItems = new TaskItem<TResult>[tasks.Length];
            for (int i = 0; i < tasks.Length; i++)
            {
                taskItems[i] = new TaskItem<TResult>(i, tasks[i]);
            }

            var results = new TResult[tasks.Length];
            var queue = new ConcurrentQueue<TaskItem<TResult>>(taskItems);
            var threads = new Task[threadNum];
            for (int i = 0; i < threadNum; i++)
            {
                threads[i] = Task.Run(() => RunTaskInternal(queue, results));
            }
            // TODO VB中使用异步等待会导致软件卡死，不清楚是什么原因
            // await threads[0];
            threads[0].Wait();

            if (threadNum > 1)
                Task.WaitAll(threads);

            return Task.FromResult(results);
        }

        private class TaskItem<TResult>
        {
            public TaskItem(int index, Func<Task<TResult>> item)
            {
                Index = index;
                Item = item;
            }

            public Func<Task<TResult>> Item { get; }
            public int Index { get; }
        }
    }
}
